package cn.willcream.sfmg.part;

import cn.org.atool.fluent.mybatis.annotation.FluentMybatis;
import cn.org.atool.fluent.mybatis.annotation.TableId;
import cn.org.atool.fluent.mybatis.base.RichEntity;
import com.squareup.javapoet.*;
import schemacrawler.schema.Column;
import schemacrawler.schema.Table;

import javax.lang.model.element.Modifier;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public abstract class AbstractCodeGenPart implements CodeGenPart {
    public AbstractCodeGenPart() {
    }

    public class FieldInfo {
        public String name;
        public List<Class<? extends Annotation>> annotations;
        public String comment;
        public Type type;
    }


    @Override
    public TypeSpec.Builder genClassSkeleton(Table t) {
        TypeSpec.Builder builder = TypeSpec.classBuilder(genClassName(t));
        builder.addModifiers(Modifier.PUBLIC)
                .addJavadoc(genClassComment(t))
                .superclass(genSuperClass(t));
        Optional.of(genInterface(t)).ifPresent(x -> {
            for (TypeName tn : x) {
                builder.addSuperinterface(tn);
            }
        });

        for (Class a : genClassAnnotation(t)) {
            builder.addAnnotation(a);
        }
        genImplement(t, builder);

        return builder;
    }

    private String castTableName(Table t) {
        return lowerUnderScoreToCamel(t.getName(), true);
    }

    /**
     * 将表注释作为类的注释
     *
     * @param t
     * @return
     */
    protected String genClassComment(Table t) {
        return t.getRemarks();
    }

    /**
     * 为类添加注解
     *
     * @param t
     * @return
     */
    protected List<Class<? extends Annotation>> genClassAnnotation(Table t) {
        return Arrays.asList(FluentMybatis.class);
    }

    /**
     * 根据表名生成类名，可以在这里添加前缀和后缀什么的
     *
     * @param t
     * @return
     */
    protected String genClassName(Table t) {
        return castTableName(t) + "Entity";
    }

    protected Type genSuperClass(Table t) {
        return RichEntity.class;
    }

    /**
     * 生成类的接口，如果有接口，必须实现{@link AbstractCodeGenPart#genImplement(schemacrawler.schema.Table, com.squareup.javapoet.TypeSpec.Builder)}
     *
     * @param t
     * @return
     */
    protected TypeName[] genInterface(Table t) {
        // get(Func.class,String.class) => Func<String>
        TypeName[] typeNames = new TypeName[]{
                ParameterizedTypeName.get(Serializable.class)
        };
        return typeNames;
    }

    /**
     * 实现继承的接口
     *
     * @param t
     * @param builder
     */
    protected void genImplement(Table t, TypeSpec.Builder builder) {
        // do nothing
    }

    @Override
    public FieldSpec genField(Column c) {
        FieldInfo fi = new FieldInfo();
        fi.comment = genFieldComment(c);
        fi.name = genFieldName(c);
        fi.annotations = genFieldAnnotation(c);
        fi.type = genFieldType(c);

        FieldSpec.Builder fieldBuilder = FieldSpec.builder(fi.type, fi.name, Modifier.PRIVATE);
        for (Class t : fi.annotations) {
            fieldBuilder.addAnnotation(t);
        }
        fieldBuilder.addJavadoc(fi.comment);
        return fieldBuilder.build();
    }

    protected String genFieldName(Column c) {
        return lowerUnderScoreToCamel(c.getName(), false);
    }

    protected String genFieldComment(Column c) {
        return c.getRemarks();
    }


    protected List<Class<? extends Annotation>> genFieldAnnotation(Column c) {
        List<Class<? extends Annotation>> list = new ArrayList<>();
        if (c.isPartOfPrimaryKey()) {
            list.add(TableId.class);
        }
        return list;
    }


    protected Type genFieldType(Column c) {
        Class cls = c.getType().getJavaSqlType().getDefaultMappedClass();
        if (Timestamp.class.equals(cls)) {
            cls = LocalDateTime.class;
        }
        return cls;
    }

    @Override
    public void genFieldGetterAndSetter(Column c, TypeSpec.Builder builder) {
        MethodSpec getter = MethodSpec.methodBuilder("get" + lowerUnderScoreToCamel(c.getName(), true))
                .addModifiers(Modifier.PUBLIC)
                .addStatement("return " + genFieldName(c))
                .returns(genFieldType(c))
                .build();

        MethodSpec setter = MethodSpec.methodBuilder("set" + lowerUnderScoreToCamel(c.getName(), true))
                .addModifiers(Modifier.PUBLIC)
                .addParameter(genFieldType(c), "value")
                .addStatement("this." + genFieldName(c) + " = value")
                .build();

        builder.addMethod(getter).addMethod(setter);
    }

}
